﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO.IsolatedStorage;
using System.Linq;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace System.IO.IsolatedStorage
{
    public static class PSDirectory
    {
        private const string BaseIsoStorePath = @"";
        private const string STR_IsolatedStoreFileName = "IsolatedStore";

        internal static string FixRelativePath(string path)
        {
            // <pex>
            Debug.Assert(path != (string)null, "path");
            // </pex>
            if (!path.StartsWith(@"\")) // Doesn't begin with "\" so relative append cwd
                return CurrentDirectory + (CurrentDirectory.EndsWith(@"\") ? "" : @"\") + path;
            return path;
        }

        internal static string FixPath(string path)
        {
            var cleanedDir = FixRelativePath(path);
            if (cleanedDir.StartsWith(@"\"))
                cleanedDir = BaseIsoStorePath + cleanedDir.Substring(1); // Now make sure that the path doesn't begin with a "\"
            if (cleanedDir == "")
            {
                cleanedDir = STR_IsolatedStoreFileName;
            }

            return cleanedDir;
        }

        public static PSDirectoryInfo CreateDirectory(string path)
        {
            var cleanedDir = FixPath(path);
            using (var store = IsolatedStorageFile.GetUserStoreForApplication())
            {
                if (!store.DirectoryExists(cleanedDir))
                {
                    store.CreateDirectory(cleanedDir.EndsWith(@"\") ? cleanedDir.Substring(0, cleanedDir.Length - 1) : cleanedDir);
                    return new PSDirectoryInfo(EnsureDirEndsWithSlash(path));
                }
                return new PSDirectoryInfo(FixRelativePath(EnsureDirEndsWithSlash(path)));
            }
        }

        public static void Delete(string path)
        {
            var cleanedDir = FixPath(path);
            using (var store = IsolatedStorageFile.GetUserStoreForApplication())
            {
                store.DeleteDirectory(removeTrailingSlash(cleanedDir));
            }
        }

        private static string removeTrailingSlash(string cleanedDir)
        {
            if (cleanedDir.EndsWith(@"\"))
            {
                return cleanedDir.TrimEnd(new[] { '\\' });
            }
            return cleanedDir;
        }

        public static void Delete(string path, bool recursive)
        {
            // <pex>
            if (path == (string)null)
                throw new ArgumentNullException("path");
            // </pex>
            if (!recursive)
            {
                Delete(path);
            }
            else
            {
                foreach (var file in GetFiles(path))
                {
                    PSFile.Delete(file);
                }

                foreach (var dir in GetDirectories(path))
                {
                    Delete(dir, true);
                }
                var cleanedDir = FixPath(path);
                using (var store = IsolatedStorageFile.GetUserStoreForApplication())
                {
                    store.DeleteDirectory(cleanedDir);
                }
            }
        }

        public static bool Exists(string path)
        {
            var cleanedDir = FixPath(path);
            using (var store = IsolatedStorageFile.GetUserStoreForApplication())
            {
                return (store.DirectoryExists(cleanedDir));
            }
        }

        public static DateTime GetCreationTime(string path)
        {
#if !WINDOWS_PHONE
            var fixedFilePath = PSDirectory.FixPath(path);
            return IsolatedStorageFile.GetUserStoreForApplication().GetCreationTime(fixedFilePath).LocalDateTime;
#else
            return DateTime.Now; // Non existent API in WiNdows Phone
#endif
        }

        internal static string CurrentDirectory = @"\";

        public static string GetCurrentDirectory()
        {
            return EnsureDirEndsWithSlash(CurrentDirectory);
        }

        public static string[] GetDirectories(string path)
        {
            var rootedPath = FixRelativePath(path);
            var cleanedDir = FixPath(path);
            using (var store = IsolatedStorageFile.GetUserStoreForApplication())
            {


                var dirs = (cleanedDir == STR_IsolatedStoreFileName) ? store.GetDirectoryNames("*") : store.GetDirectoryNames(cleanedDir);

                return EnsureDirsEndWithSlash(EnsureDirNamesHaveSearchPath(rootedPath, dirs));
            }
        }

        public static string[] GetDirectories(string path, string searchPattern)
        {
            if (searchPattern.Contains(@"\"))
            {
                throw new System.IO.IOException(@"Search pattern cannot contain a \");
            }

            var rootedPath = FixRelativePath(path);
            var cleanedDir = FixPath(path);
            using (var store = IsolatedStorageFile.GetUserStoreForApplication())
            {
                //var searchPath = cleanedDir + (cleanedDir.Length == 0 || cleanedDir.EndsWith(@"\") ? "" : @"\") + searchPattern;
                var dirs = store.GetDirectoryNames(cleanedDir);

                return EnsureDirsEndWithSlash(EnsureDirNamesHaveSearchPath(rootedPath, WildcardMatch(dirs, searchPattern).ToArray()));
            }
        }

        private static string[] EnsureDirsEndWithSlash(string[] dirs)
        {
            for (int i = 0; i < dirs.Length; i++)
            {
                dirs[i] = EnsureDirEndsWithSlash(dirs[i]);
            }
            return dirs;
        }

        private static string EnsureDirEndsWithSlash(string dir)
        {
            return !dir.EndsWith(@"\") ? dir + @"\" : dir;
        }

        private static string[] EnsureDirNamesHaveSearchPath(string searchPath, string[] dirs)
        {
            var lastSlash = searchPath.LastIndexOf('\\');
            if (lastSlash != -1)
            {
                searchPath = searchPath.Substring(0, lastSlash);
            }
            if (searchPath.EndsWith("*"))
            {
                searchPath = searchPath.Substring(0, searchPath.Length - 1);
            }
            if (!searchPath.StartsWith(@"\"))
                searchPath = @"\" + searchPath;
            if (!searchPath.EndsWith(@"\"))
                searchPath = searchPath + @"\";

            if (dirs.Length < 1 || dirs[0].StartsWith(searchPath))
            {
                return dirs;
            }
            for (int i = 0; i < dirs.Length; i++)
            {
                dirs[i] = searchPath + dirs[i];
            }
            return dirs;
        }

        public static string GetDirectoryRoot()
        {
            return @"\";  // Not exactly to spec, but should really never be anything other than this
        }

        public static string[] GetFiles(string path)
        {
            var rootedPath = FixRelativePath(path);
            var cleanedDir = FixPath(path);
            using (var store = IsolatedStorageFile.GetUserStoreForApplication())
            {
                //var searchPath = cleanedDir + (cleanedDir.Length == 0 || cleanedDir.EndsWith(@"\") ? "" : @"\") + searchPattern;

                var dirs = (cleanedDir.EndsWith(@"\")) ? store.GetFileNames(cleanedDir + "*") : store.GetFileNames(cleanedDir);

                return EnsureDirNamesHaveSearchPath(rootedPath, dirs);
            }
        }

        public static string[] GetFiles(string path, string searchPattern)
        {
            // <pex>
            if (searchPattern == (string)null)
                throw new ArgumentNullException("searchPattern");
            // </pex>
            if (searchPattern.Contains(@"\"))
            {
                throw new System.IO.IOException(@"Search pattern cannot contain a \");
            }
            var rootedPath = FixRelativePath(path);
            var cleanedDir = FixPath(path);
            //string[] result;
            using (var store = IsolatedStorageFile.GetUserStoreForApplication())
            {
                //var searchPath = cleanedDir + (cleanedDir.Length == 0 || cleanedDir.EndsWith(@"\") ? "" : @"\") + searchPattern;
                var dirs = store.GetFileNames(cleanedDir);

                return EnsureDirsEndWithSlash(EnsureDirNamesHaveSearchPath(rootedPath, WildcardMatch(dirs, searchPattern).ToArray()));
            }
        }

        public static AbstractFileSystemInfo[] GetFileSystemEntries(string path)
        {
            // <pex>
            if (path == (string)null)
                throw new ArgumentNullException("path");
            // </pex>

            var result = (from item in GetDirectories(path)
                          select (AbstractFileSystemInfo)(new PSDirectoryInfo(item))).ToList();
            var fls = GetFiles(path);
            result.AddRange(from item in fls select (AbstractFileSystemInfo)(new PSFileInfo(item)));
            return result.ToArray();
        }

        public static AbstractFileSystemInfo[] GetFileSystemEntries(string path, string searchPattern)
        {
            // <pex>
            if (searchPattern == (string)null)
                throw new ArgumentNullException("searchPattern");
            if (path == (string)null)
                throw new ArgumentNullException("path");
            // </pex>
            var result = (from item in GetDirectories(path, searchPattern)
                          select (AbstractFileSystemInfo)(new PSDirectoryInfo(item))).ToList();
            result.AddRange(from item in GetFiles(path, searchPattern) select (AbstractFileSystemInfo)(new PSFileInfo(item)));
            return result.ToArray();
        }

        public static DateTime GetLastAccessTime(string path)
        {
#if !WINDOWS_PHONE

            var fixedFilePath = PSDirectory.FixPath(path);
            return IsolatedStorageFile.GetUserStoreForApplication().GetLastAccessTime(fixedFilePath).LocalDateTime;
#else
            return DateTime.Now; // Non existent API in WiNdows Phone
#endif
        }

        public static DateTime GetLastWriteTime(string path)
        {
#if !WINDOWS_PHONE
            var fixedFilePath = PSDirectory.FixPath(path);
            return IsolatedStorageFile.GetUserStoreForApplication().GetLastWriteTime(fixedFilePath).LocalDateTime;
#else
            return DateTime.Now; // Non existent API in WiNdows Phone
#endif
        }

        public static void Move(string sourceDirName, string destDirName)
        {
            var cleanedDir = FixPath(sourceDirName);
            var destCleanedDir = FixPath(sourceDirName);

            using (var store = IsolatedStorageFile.GetUserStoreForApplication())
            {
                store.MoveDirectory(cleanedDir, destCleanedDir);
            }
        }

        public static void SetCurrentDirectory(string path)
        {
            path = FixRelativePath(path);
            CurrentDirectory = path;
        }

        private static Regex WildcardToRegEx(string pattern)
        {
            // <pex>
            Debug.Assert(pattern != (string)null, "pattern");
            // </pex>
            return new Regex("^" + Regex.Escape(pattern).
                                        Replace("\\*", ".*").
                                        Replace("\\?", ".") + "$", RegexOptions.IgnoreCase);
        }

        public static List<string> WildcardMatch(string[] filterthis, string pattern)
        {
            // <pex>
            if (filterthis == (string[])null)
                throw new ArgumentNullException("filterthis");
            // </pex>
            List<string> result = new List<string>(filterthis.Length);
            var regexp = WildcardToRegEx(pattern);

            for (int i = 0; i < filterthis.Length; i++)
            {
                if (regexp.IsMatch(filterthis[i]))
                {
                    result.Add(filterthis[i]);
                }
            }
            return result;
        }

        //#if(!WINDOWS_PHONE)

        //        public static long SystemFreeSpace
        //        {
        //            get {
        //                using (var store = IsolatedStorageFile.GetUserStoreForApplication())
        //                {
        //                    return store.AvailableFreeSpace;
        //                }
        //            }
        //        }

        //        public static long MaxSpaceAllocated
        //        {
        //            get
        //            {
        //                using (var store = IsolatedStorageFile.GetUserStoreForApplication())
        //                {
        //                    return store.Quota;
        //                }
        //            }
        //        }
        //        public static long StorageSpaceUsed
        //        {
        //            get
        //            {
        //                using (var store = IsolatedStorageFile.GetUserStoreForApplication())
        //                {
        //                    return store.UsedSize;
        //                }
        //            }
        //        }
        //        public static long FreeSpace
        //        {
        //            get
        //            {
        //                using (var store = IsolatedStorageFile.GetUserStoreForApplication())
        //                {
        //                    return store.Quota - store.UsedSize;
        //                }
        //            }
        //        }
        //        private static long autoAllocateWhenSpaceDropsBelow = 1048576; //1024 * 1024; // 1mb
        //        public static long AutoAllocateWhenSpaceDropsBelow
        //        {
        //            get
        //            {
        //                return autoAllocateWhenSpaceDropsBelow;
        //            }
        //            set
        //            {
        //                autoAllocateWhenSpaceDropsBelow = value;
        //            }
        //        }
        //        private static long autoAllocateAmountToIncreaseBy = 5242880; //5mb
        //        public static long AutoAllocateAmountToIncreaseBy
        //        {
        //            get
        //            {
        //                return autoAllocateAmountToIncreaseBy;
        //            }
        //            set
        //            {
        //                autoAllocateAmountToIncreaseBy = value;
        //            }
        //        }
        //        internal static void DoAutoAllocCheck()
        //        {
        //            DoAutoAllocCheck(0);
        //        }
        //        internal static void DoAutoAllocCheck(long NextActionWillUseNBytes)
        //        {
        //            if (AutoAllocateAmountToIncreaseBy < 0l || AutoAllocateWhenSpaceDropsBelow < 0l)
        //                return;
        //            if (FreeSpace - NextActionWillUseNBytes < AutoAllocateWhenSpaceDropsBelow)
        //            {
        //                using (var store = IsolatedStorageFile.GetUserStoreForApplication())
        //                {
        //                    store.IncreaseQuotaTo(store.Quota + AutoAllocateAmountToIncreaseBy);
        //                }
        //            }
        //        }
        //#endi
    }

#if WINDOWS_PHONE

    internal static class IsoStorDirExtensions
    {
        public static void MoveDirectory(this IsolatedStorageFile store, string sourceDir, string destDir)
        {
            //TODO: Need to implement this
            throw new NotImplementedException();
        }
    }

#endif
}